iT邦幫忙

2024 iThome 鐵人賽

DAY 22
1

在ES6引入class之前,JavaScript使用建構函式和原型來模擬類別繼承。後來有class語法後,提供了更簡潔、可讀性更高的方式。

先介紹先前的寫法~

過去寫法

過去兩個建構函式要共用屬性與方法,需要以下步驟才能:

  1. 彼此的原型要連結。
  2. 彼此共用屬性,讓後代繼承前代屬性。

範例:

function Animal(breed){
    this.breed = breed;
}

function Dog(name,breed){
   this.name = name;
   Animal.call(this,breed)
}

先建立兩個建構函式Animal和Dog,他們彼此沒有任何關聯。

Dog.prototype = Object.create(Animal.prototype);

Dog.prototype.constructor = Dog;

現在要讓Dog繼承Animal。先前有說到,Object.create()它會建立一個新的空物件,而空物件的原型繼承指定傳入的物件。

所以讓Dog.prototype繼承Animal.prototype,但這樣會讓Dog上的prototype屬性的 constructor 會不見,如圖:
constructor不見

constructor 他原本預設要指回Dog函式。所以再把他指回去Dog函式,也就是Dog.prototype.constructor = Dog

執行結果:
constructor指回
看到Dog的prototype裡面的constructor回來了~

Animal.prototype.sleep = function(){
    console.log("睡覺zzz");
} 

Dog.prototype.bark = function(){
    console.log("汪!");
}

const puppy = new Dog("點點","chiwawa");

為了方便測試,在Animal的prototype上加上了sleep方法,Dog的prototype加上bark方法,再用new Dog建立實例puppy

puppy.sleep();
puppy.bark();

執行puppy.sleep()和puppy.bark(),看看原型的方法能不能使用。

執行結果:
執行結果
puppy.sleep()和puppy.bark()皆執行成功~

console.log(puppy);

//Dog {name: '點點', breed: 'chiwawa'}

再來印出puppy看看,確認有拿到Animal的breed屬性。
以上就是傳統寫法~

有class的寫法

接下來改用class語法糖,幫助我們程式碼更加簡潔,不用修改原型物件,也可以有繼承。

語法

class name {
  // class body
}
class name extends otherName {
  // class body
}

把剛剛的題目改成class寫法:

//定義前代類別Animal
class Animal {
  constructor(breed) {
    this.breed = breed;
  }
  sleep() {
    console.log("睡覺zzz"); //定義睡覺方法
  }
}

//定義後代類別Dog,並繼承Animal
class Dog extends Animal {
  constructor(breed, name) {
    super(breed); //呼叫前代類別的constructor 
    this.name = name; //後代類別的屬性
  }
  bark() {
    console.log("汪!"); //定義叫聲方法
  }
}
const puppy = new Dog("chiwawa", "點點");

裡面有幾個關鍵字需要知道:

  1. extends:class只需使用extends關鍵字就能繼承。所以class Dog extends Animal也就是Dog繼承自Animal,很直觀的寫法。
  2. constructor():放原本建構函式的邏輯。例如:Animal建構函式原本的this.breed = breed就放在這裡面。
  3. super():是前代類別的 constructor。在後代類別的 constructor中呼叫 super() 就能繼承前代類別的屬性。
puppy.sleep();
puppy.bark();

//"睡覺zzz"
//"汪!"

sleep()方法定義在Animal上,bark()方法定義在Dog的原型上,所有Dog的實例都可以呼叫這兩種方法。

console.log(Animal.prototype.sleep); 
//sleep(){console.log("睡覺zzz");}

console.log(Dog.prototype.bark); 
//bark(){console.log("汪!");}

用console.log 檢查 sleep() 和 bark() 是否定義在 prototype上,結果正確。

console.log(puppy);

//Dog {breed: 'chiwawa', name: '點點'}

最後確認印出puppy,跟傳統寫法結果一樣~

補充

  • class要搭配new關鍵字使用,忘記加會報錯。
const dog1 = Dog("小黑","Formosan Mountain Dog");

//TypeError: Class constructor Dog cannot be invoked without 'new'
  • super()必須在this關鍵字之前呼叫,否則會報錯。
class Dog extends Animal {
  constructor(breed, name) { 
    this.name = name; //後代類別的屬性
    super(breed); //呼叫前代類別
  }
    
//ReferenceError: Must call super constructor in derived class before accessing 'this' or returning from derived constructor

以上分享~謝謝!

參考資料

MDN - class
JS 原力覺醒 Day23 - Class 語法糖


上一篇
[Day 21] 建構函式 constructor function
下一篇
[Day 23] 動手做一個new 運算子吧!
系列文
JavaScript學習筆記30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言